package com.leilei.entity;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author: lei
 * @date: 2025-05-21 10:46
 * @desc: 字段处理器，用于对实体对象中标记了 @EncryptField 注解的字段进行加解密
 */
@Slf4j
public class EncryptProcessor {


    public enum Mode {ENCRYPT, DECRYPT}


    private final Sm4Encryptor encryptor;
    private final Map<Class<?>, List<Field>> FIELD_CACHE = new ConcurrentHashMap<>();

    public EncryptProcessor(Sm4Encryptor encryptor) {
        this.encryptor = encryptor;
    }

    /**
     * 使用反射,对目标对象的所有标记字段进行加密或解密处理
     * @author lei
     * @date 2025-05-21 11:10:53
     * @param target
     * @param mode
     * @return void
     */
    public  void process(Object target, Mode mode) {
        if (target == null) {
            return;
        }
        Class<?> current = target.getClass();
        List<Field> targetFields = FIELD_CACHE.computeIfAbsent(current, EncryptProcessor::findEncryptFields);
        // 遍历对象及其所有父类的字段
        for (Field field : targetFields) {
            try {
                String value = (String) field.get(target);
                if (value != null) {
                    if (mode == Mode.ENCRYPT) {
                        // 加密
                        String encrypted = encryptor.encrypt(value);
                        field.set(target, encrypted);
                    }
                    if (mode == Mode.DECRYPT) {
                        // 解密
                        String decrypted = encryptor.decrypt(value);
                        field.set(target, decrypted);
                    }
                }
            } catch (IllegalAccessException e) {
                log.error("IllegalAccessException", e);
            } catch (Exception e) {
                log.error("DB字段加解密异常", e);
            }
        }
    }

    /**
     * 查找指定目标类加了@EncryptField注解的字段
     *
     * @param clazz
     * @return List<Field>
     * @author lei
     * @date 2025-05-21 09:49:31
     */
    private static List<Field> findEncryptFields(Class<?> clazz) {
        List<Field> result = new ArrayList<>();
        while (clazz != null && clazz != Object.class) {
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(EncryptField.class) && field.getType().equals(String.class)) {
                    field.setAccessible(true);
                    result.add(field);
                }
            }
            clazz = clazz.getSuperclass();
        }
        return result;
    }
}
